How Do I Add an Ellipses Button to an Editor?

You can add a Button to your custom editor template in the normal way, and hook up the Click event to a handler that displays a dialog:

CopyXML
<DataTemplate x:Key="MyEditor">
  <DockPanel>
    <Button DockPanel.Dock="Right" Click="DlgButton_Click">...</Button>
    <TextBlock Text="{Binding Value}" />
  </DockPanel>
</DataTemplate>

The challenge now is to transport data between the edited property and the dialog.

The exact way of getting data into and out of the dialog will depend on your dialog design (constructor parameter? a property? setter/getter methods?), and your Click event handler will have to handle that.

That just leaves getting the data into and out of the edited property. The easiest way to do this is to observe that the sender parameter of your Click event handler is the button. This means the Click event handler can get and set properties of the button. If those properties are data bound to the edited property, the Click event handler can use them to get and set the property value.

A simple solution is to use the Tag property:

CopyXML
<Button DockPanel.Dock="Right" Click="DlgButton_Click" 
        Tag="{Binding Value, Mode=TwoWay}" 
        >...</Button>

The Click event now looks like this:

CopyC#
private void DlgButton_Click(object sender, RoutedEventArgs e)
{
  Button button = (Button)sender;
  string value = (string)(button.Tag);

  // Display dialog UI and get new value here - will depend on your design
  string newValue = ShowDialogWithInitialValue(value);

  button.Tag = newValue;
}

Because the Tag property has a two-way binding, setting the Tag property propagates the new value back into the edited property.

The only issue with this is that the Tag property doesn’t have well-defined semantics, so in complex applications it’s possibly that somebody else might decide to use Tag for something else. (A lesser limitation is that Tag can hold only a single object: what if you want to pass both the property value and the property metadata?) You can address this by creating an attached property and setting that on the button instead of the Tag. (If you need to pass multiple pieces of data just create more attached properties.) Thus:

CopyC#
// Attached property declaration
public static class DialogEditor
{
  public static readonly DependencyProperty ValueProperty =
    DependencyProperty.RegisterAttached("Value", typeof(object),
    typeof(DialogEditor),
    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

  public static object GetValue(DependencyObject obj)
  {
    return obj.GetValue(ValueProperty);
  }

  public static void SetValue(DependencyObject obj, object value)
  {
    obj.SetValue(ValueProperty, value);
  }
}
CopyXML
// Editor template
<Button DockPanel.Dock="Right" Click="DlgButton_Click" 
        local:DialogEditor.Value="{Binding Value}" 
        >...</Button>
CopyC#
// Code-behind
private void DlgButton_Click(object sender, RoutedEventArgs e)
{
  Button button = (Button)sender;
  string value = (string)(DialogEditor.GetValue(button));

  // Display dialog UI and get new value here - will depend on your design
  string newValue = ShowDialogWithInitialValue(value);

  DialogEditor.SetValue(button, newValue);
}